package com.wiscamp.ninechapters.problems.application.services;

import com.wiscamp.ninechapters.common.core.CollectionHelper;
import com.wiscamp.ninechapters.problems.application.assemblers.ProblemAssembler;
import com.wiscamp.ninechapters.problems.application.assemblers.ProblemCollectionAssembler;
import com.wiscamp.ninechapters.problems.application.dto.commands.collections.*;
import com.wiscamp.ninechapters.problems.application.dto.vo.collections.CollectionProblemVO;
import com.wiscamp.ninechapters.problems.application.dto.vo.collections.ProblemCollectionVO;
import com.wiscamp.ninechapters.problems.application.dto.vo.problems.ProblemVO;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemCollectionRepository;
import com.wiscamp.ninechapters.problems.domain.services.ProblemCollectionService;
import com.wiscamp.ninechapters.problems.domain.services.ProblemService;
import cube.ddd.application.BaseAppService;
import cube.ddd.application.command.DeleteCommand;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.AppServiceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;

import java.util.Collection;

@Service
public class ProblemCollectionAppService extends BaseAppService {

    private final ProblemCollectionRepository problemCollectionRepository;

    private final ProblemCollectionService problemCollectionService;

    private final ProblemService problemService;

    public ProblemCollectionAppService(ProblemCollectionRepository problemCollectionRepository, ProblemCollectionService problemCollectionService, ProblemService problemService) {
        this.problemCollectionRepository = problemCollectionRepository;
        this.problemCollectionService = problemCollectionService;
        this.problemService = problemService;
    }


    // region problem collection
    @HandleServiceException
    public Collection<Long> getProblemIdsByCollection(long collectionId) {
        return problemCollectionRepository.getProblemIdsByCollection(collectionId);
    }

    @HandleServiceException
    public Collection<ProblemVO> getProblemsByCollection(long collectionId) {
        var problemIds = problemCollectionRepository.getProblemIdsByCollection(collectionId);
        var problems = problemService.getProblemsByIds(problemIds);
        return CollectionHelper.convertToArrayList(problems, ProblemAssembler.Instance::toProblemVO);
    }

    @HandleServiceException
    public ProblemCollectionVO getProblemCollection(long collectionId) {
        return AppServiceUtils.getById(collectionId, problemCollectionRepository::getProblemCollection,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public ProblemCollectionVO getProblemCollectionByCode(@NotBlank String collectionCode, @Min(0) long organizationId) {
        return AppServiceUtils.getByTextAndId(collectionCode, organizationId, problemCollectionRepository::getProblemCollectionByCode,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }


    @HandleServiceException
    public Collection<ProblemCollectionVO> getProblemCollectionsByName(@NotBlank String collectionName, @Min(0) long organizationId) {
        return AppServiceUtils.getListByTextAndId(collectionName, organizationId, problemCollectionRepository::getProblemCollectionsByName,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }


    @HandleServiceException
    public Collection<ProblemCollectionVO> getProblemCollectionsByNameContains(@NotBlank String name, @Min(0) long organizationId) {
        return AppServiceUtils.getListByTextAndId(name, organizationId, problemCollectionRepository::getProblemCollectionsByNameContains,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public Collection<ProblemCollectionVO> getProblemCollectionsByNameContains(@NotBlank String namePrefix, @Min(0) long organizationId, @NotBlank String gradeLevel) {
        var list = problemCollectionRepository.getProblemCollectionsByNameContains(namePrefix, organizationId, gradeLevel);
        return CollectionHelper.convertToArrayList(list, ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public Collection<ProblemCollectionVO> getProblemCollectionsByOrganization(@Min(0) long organizationId) {
        return AppServiceUtils.getListById(organizationId, problemCollectionRepository::getProblemCollectionsByOrganization,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public Collection<ProblemCollectionVO> getProblemCollectionsByExam(@Min(0) long examId) {
        return AppServiceUtils.getListById(examId, problemCollectionRepository::getProblemCollectionsByExam,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public PagedObjects<ProblemCollectionVO> getProblemCollectionsByQuery(@NotNull PagedQuery query) {
        var pagedEntities = problemCollectionRepository.getProblemCollectionsByQuery(query);
        return AppServiceUtils.convertPagedObjects(pagedEntities, ProblemCollectionAssembler.Instance::toProblemCollectionVO);
    }

    @HandleServiceException
    public ProblemCollectionVO createProblemCollection(@NotNull CreateProblemCollectionCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), problemCollectionService::createProblemCollection,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO, ProblemCollectionAssembler.Instance::fromCreateCommand);
    }

    @HandleServiceException
    public ProblemCollectionVO updateProblemCollection(@NotNull UpdateProblemCollectionCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), problemCollectionService::updateProblemCollection,
                ProblemCollectionAssembler.Instance::toProblemCollectionVO, ProblemCollectionAssembler.Instance::fromUpdateCommand);
    }

    @HandleServiceException
    public boolean deleteProblemCollection(@NotNull DeleteCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.delete(command.getId(), command.getOperatorId(), problemCollectionService::deleteProblemCollection);
    }

    // endregion

    // region collection problem
    @HandleServiceException
    public int getMaxProblemIndex(@Min(0) long collectionId) {
        return problemCollectionRepository.getMaxProblemIndex(collectionId);
    }

    @HandleServiceException
    public Collection<CollectionProblemVO> getCollectionProblems(long collectionId) {
        var collectionProblems = problemCollectionRepository.getCollectionProblems(collectionId);
        return CollectionHelper.convertToArrayList(collectionProblems, ProblemCollectionAssembler.Instance::toCollectionProblemVO);
    }

    @HandleServiceException
    public long getNextProblemId(@Min(0) long collectionId, @Min(0) long problemId) {
        return problemCollectionService.getNextProblemId(collectionId, problemId);
    }
    @HandleServiceException
    public boolean removeProblem(RemoveCollectionProblemCommand command) {
        command.setOperatorId(getCurrentUserId());
        return problemCollectionService.removeProblem(command.getCollectionId(), command.getProblemId(), command.getOperatorId());
    }

    // 兼容旧前端接口
    @HandleServiceException
    public boolean removeProblem(long problemId) {
        var collectionProblem = problemCollectionRepository.getCollectionProblemByProblem(problemId);
        return problemCollectionService.removeProblem(collectionProblem.getCollectionId(), problemId, 0);
    }
    // endregion


    // region batch tag collection
    @HandleServiceException
    public boolean batchTagProblems(@NotNull BatchTagCommand command) {
        command.setOperatorId(getCurrentUserId());
        return problemCollectionService.batchTagProblems(command.getCollectionId(), command.getTagGroupName(), command.getTagName());
    }

    @HandleServiceException
    public boolean batchUntagProblems(@NotNull BatchUntagCommand command) {
        command.setOperatorId(getCurrentUserId());
        return problemCollectionService.batchUntagProblems(command.getCollectionId(), command.getTagGroupName(), command.getTagName());
    }
    // endregion
}
